package jflowmap.views.heatmap;

import java.awt.Color;
import java.awt.Font;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import jflowmap.model.graph.Edge;
import jflowmap.model.graph.Node;
import jflowmap.model.ontology.Dimension;
import jflowmap.model.ontology.SelectionListener;
import jflowmap.util.InformativeComparator;
import jflowmap.util.piccolo.PNodes;
import jflowmap.views.Disposable;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;

import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.nodes.PText;

public class HeatMapRow extends PNode implements SelectionListener, Disposable  {
	public final static InformativeComparator<HeatMapRow> COMPARE_BY_NAME = new InformativeComparator<HeatMapRow>() {
		@Override
		public int compare(HeatMapRow o1, HeatMapRow o2) {
			return o1.target.getDimension().compareTo(o2.target.getDimension());
		}

		@Override
		public String getDescription() {
			return "alphabetisch gereiht";
		}
	};
	
	private static final Font TARGET_NAME_FONT = new Font("Arial", Font.BOLD, 14);
	
	private InformativeComparator<SingleHeatMapRow> singleRowComparator = SingleHeatMapRow.COMPARE_BY_MEASUREMENT;
	private final HeatMapLayer heatMap;
	private final Node target;
	private final List<Node> sources;
	private final Map<Dimension, SingleHeatMapRow> singleRows = Maps.newTreeMap();
	
	private final PText targetName;
	
	private double maxCellWidth = 0;
	
	public HeatMapRow(Node target, HeatMapLayer heatMap) {
		this.heatMap = heatMap;
		this.target = target;
		this.sources = Lists.newArrayListWithCapacity(target.getIncomingEdges().size());
		
		for (Edge edge: target.sortIncomingEdges(Ordering.usingToString())) {
			singleRows.put(edge.getSourceNode().getDimension(), createSingleRowFor(edge));
		}
		
		this.targetName = new PText(target.getDimension().getName());
		this.targetName.setFont(TARGET_NAME_FONT);
		this.addChild(targetName);
		
		heatMap.getInfo().onClick("Reihung:",
				new Runnable() {
					@Override
					public void run() {
						final PInfo info = HeatMapRow.this.heatMap.getInfo();
						
						if (getSingleRowComparator() == SingleHeatMapRow.COMPARE_BY_MEASUREMENT) {
							setSingleRowComparator(SingleHeatMapRow.COMPARE_BY_NAME);
							info.set("Reihung:", SingleHeatMapRow.COMPARE_BY_NAME.getDescription());
							
						} else if (getSingleRowComparator() == SingleHeatMapRow.COMPARE_BY_NAME) {
							setSingleRowComparator(SingleHeatMapRow.COMPARE_BY_AVERAGE);
							info.set("Reihung:", SingleHeatMapRow.COMPARE_BY_AVERAGE.getDescription());
							
						} else {
							setSingleRowComparator(SingleHeatMapRow.COMPARE_BY_MEASUREMENT);
							info.set("Reihung:", SingleHeatMapRow.COMPARE_BY_MEASUREMENT.getDescription());
							
						}
						
						HeatMapRow.this.heatMap.getVisualHeatMap().update();
					}
				});
	}
	
	public InformativeComparator<SingleHeatMapRow> getSingleRowComparator () {
		return this.singleRowComparator;
	}
	
	public void setSingleRowComparator (InformativeComparator<SingleHeatMapRow> comparator) {
		this.singleRowComparator = comparator;
	}

	private SingleHeatMapRow createSingleRowFor(Edge edge) {		
		final SingleHeatMapRow singleRow = new SingleHeatMapRow(edge, heatMap);
		
		if (singleRow.getMaxCellWidth() > maxCellWidth) {
			maxCellWidth = singleRow.getMaxCellWidth();
		}
		
		final Dimension sourceDimension = edge.getSourceNode().getDimension();
		heatMap.getView().getSelection("standard").addSelectionListener(sourceDimension, this);
		if (heatMap.getView().getSelection("standard").isSelected("SOURCE", sourceDimension)) {
			this.addChild(singleRow);
		}
		
		return singleRow;
	}

	public double getMaxCellWidth() {
		return maxCellWidth;
	}

	public void setCellWidth(double cellWidth) {
		this.maxCellWidth = cellWidth;
		for (final SingleHeatMapRow singleRow: this.singleRows.values()) {
			singleRow.setCellWidth(cellWidth);
		}
	}
	
	public HeatMapCell get(Dimension sourceDimension, Dimension valueDimension) {
		return singleRows.get(sourceDimension).get(valueDimension);
	}
	
	public Node getTargetNode() {
		return target;
	}

	public void update() {		
		List<SingleHeatMapRow> singleRows = Lists.newLinkedList(PNodes.childrenOfType(this, SingleHeatMapRow.class));
		Collections.sort(singleRows, singleRowComparator);
		
		this.setHeight(0);
		double y = 0;
		
		for (SingleHeatMapRow row: singleRows) {
			row.update();
			row.setOffset(0, y);
			y += row.getFullBoundsReference().height;
		}
		
		if (singleRows.size() > 0) {
			this.targetName.setOffset(maxCellWidth * singleRows.get(0).getNumberOfVisibleCells() + 5,
									   y/2 - targetName.getHeight()/2);
		} else {
			this.targetName.setOffset(0, 0);
		}
		
		this.setPaint(Color.red);
	}

	@Override
	public void select(String category, Dimension dimension) {
		if ("SOURCE".equals(category)) {
			this.addChild(singleRows.get(dimension));
			singleRows.get(dimension).addMeasurementInfo(heatMap.getInfo());
			heatMap.getInfo().set("Reihung:", singleRowComparator.getDescription());
		}
	}

	@Override
	public void unselect(String category, Dimension dimension) {
		if ("SOURCE".equals(category)) {
			this.removeChild(singleRows.get(dimension));
			singleRows.get(dimension).removeMeasurementInfo(heatMap.getInfo());
			heatMap.getInfo().set("Reihung:", singleRowComparator.getDescription());
		}
	}

	@Override
	public void dispose() {
		for (final Dimension sourceDimension: this.singleRows.keySet()) {
			heatMap.getView().getSelection("standard").removeSelectionListener(sourceDimension, this);
		}
		
		for (final SingleHeatMapRow singleRow: singleRows.values()) {
			singleRow.dispose();
		}
	}
}
